//
//  HUMWaterView.swift
//  WaterAnimation
//
//  Created by Ming on 2018/12/5.
//  Copyright © 2018年 Insta360. All rights reserved.
//

import UIKit
import AVFoundation

class HUMWaterView: UIView {
    private lazy var waterWaveLayer: CAShapeLayer = {
        let layer = CAShapeLayer()
        layer.fillColor = UIColor(red: CGFloat(0) / 255, green: CGFloat(135) / 255, blue: CGFloat(255) / 255, alpha: 1.0).cgColor
        return layer
    }()
    private var audioPlayer: AVAudioPlayer?
    private var displayLink: CADisplayLink?
    private var waterWaveStartTime: CFAbsoluteTime = 0
    private var waterDropArray = [CALayer]()
    
    var waterDropSoundOn: Bool = false {
        didSet {
            if !waterDropSoundOn {
                audioPlayer?.stop()
                audioPlayer = nil
            }
        }
    }
    var dropDuration: CFTimeInterval = 5
    
    init() {
        super.init(frame: .zero)
        setupUI()
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupUI()
    }
    private func setupUI() {
        layer.addSublayer(waterWaveLayer)
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        waterWaveLayer.frame = bounds
        updateWaterWavePath()
    }
    
    private func startWaterWaveAnimation() {
        displayLink?.invalidate()
        displayLink = nil
        waterWaveStartTime = CFAbsoluteTimeGetCurrent()
        displayLink = CADisplayLink(target: self, selector: #selector(updateWaterWavePath))
        displayLink?.add(to: RunLoop.main, forMode: .common)
    }
    @objc private func updateWaterWavePath() {
        let width = bounds.width, height = bounds.height, halfWidth = width / 2
        var duration = CGFloat(CFAbsoluteTimeGetCurrent() - waterWaveStartTime)
        
        let waveAnimationDuration: CGFloat = 1  // 动画时长
        let maxPeriodCount: CGFloat = 4         // 水波延伸的持续的周期，默认总共显示4个水波周期
        let periodInPoint: CGFloat = 24         // 每个水波周期在屏幕上显示的点个数
        
        if duration > waveAnimationDuration || waterWaveStartTime == 0 {
            duration = waveAnimationDuration
            displayLink?.invalidate()
            displayLink = nil
        }
        
        let periodDuration = periodInPoint * waveAnimationDuration / (maxPeriodCount * periodInPoint)
        let forward = periodInPoint * duration / periodDuration
        
        // y = Asin(Bx + C) + D
        let pi: CGFloat = CGFloat(Double.pi)
        let A: CGFloat = 2
        let maxA: CGFloat = (1 - duration / waveAnimationDuration) * A
        let minA: CGFloat = 0
        let B: CGFloat = 2 * pi / periodInPoint
        let leftC: CGFloat = pi * 0.5, rightC: CGFloat = pi * 0.5
        let D: CGFloat = 0
        
        let path = UIBezierPath()
        let waveMaxDistance = periodInPoint * maxPeriodCount
        // left wave
        let leftFirstX = halfWidth
        path.move(to: CGPoint(x: 0, y: height / 2))
        let leftMinX = max(leftFirstX - waveMaxDistance, 0)
        for i in Int(leftMinX) ... Int(leftFirstX) {
            let x = CGFloat(i)
            let currentA = maxA - (maxA - minA) * (leftFirstX - x) / waveMaxDistance
            let y = height / 2 + currentA * sin(B * (leftFirstX - x - forward) + leftC) + D
            let point = CGPoint(x: x, y: y)
            path.addLine(to: point)
        }
        // right wave
        let rightFirstX = halfWidth
        let rightMaxX = min(rightFirstX + waveMaxDistance, width)
        for i in Int(rightFirstX) ... Int(rightMaxX) {
            let x = CGFloat(i) - rightFirstX
            let currentA = maxA - (maxA - minA) * x / waveMaxDistance
            let y = height / 2 + currentA * sin(B * (x - forward) + rightC) + D
            path.addLine(to: CGPoint(x: rightFirstX + x, y: y))
        }
        path.addLine(to: CGPoint(x: width, y: height / 2))
        path.addLine(to: CGPoint(x: width, y: height))
        path.addLine(to: CGPoint(x: 0, y: height))
        path.close()
        waterWaveLayer.path = path.cgPath
    }
    private func playWaterDropSound() {
        if let url = Bundle.main.url(forResource: "water-drop1", withExtension: "wav") {
            do {
                if audioPlayer == nil {
                    audioPlayer = try AVAudioPlayer(contentsOf: url)
                    audioPlayer?.volume = 1.0
                    audioPlayer?.numberOfLoops = 0
                    audioPlayer?.delegate = self
                }
                audioPlayer?.currentTime = 0
                if audioPlayer?.prepareToPlay() ?? false {
                    audioPlayer?.play()
                }
            } catch let error {
                print(error)
            }
        }
    }
}

extension HUMWaterView {
    func addWaterDrop() {
        let waterDropLayer = CALayer()
        waterDropLayer.contents = UIImage(named: "water-drop")?.cgImage
        waterDropLayer.contentsGravity = .center
        waterDropLayer.contentsScale = UIScreen.main.scale
        waterDropLayer.frame = CGRect(x: bounds.width / 2 - 12, y: 0, width: 24, height: 24)
        layer.addSublayer(waterDropLayer)
        waterDropArray.append(waterDropLayer)
        
        let scaleDuration: CFTimeInterval = dropDuration - 1 + 0.4
        let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
        scaleAnimation.fromValue = CGFloat(0.0)
        scaleAnimation.toValue = CGFloat(1.0)
        scaleAnimation.duration = scaleDuration
        scaleAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
        scaleAnimation.isRemovedOnCompletion = false
        scaleAnimation.fillMode = .forwards
        
        let transformAnimation = CABasicAnimation(keyPath: "position.y")
        transformAnimation.fromValue = CGFloat(0)
        transformAnimation.toValue = CGFloat(12)
        transformAnimation.duration = scaleDuration
        transformAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
        transformAnimation.isRemovedOnCompletion = false
        transformAnimation.fillMode = .forwards
        
        let dropdownAnimation = CABasicAnimation(keyPath: "position.y")
        dropdownAnimation.fromValue = CGFloat(12)
        dropdownAnimation.toValue = CGFloat(bounds.height / 2)
        dropdownAnimation.duration = 0.7
        dropdownAnimation.beginTime = dropDuration - 1 + 0.3
        dropdownAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn)
        dropdownAnimation.isRemovedOnCompletion = false
        dropdownAnimation.fillMode = .forwards
        
        let alphaAnimation = CABasicAnimation(keyPath: "opacity")
        alphaAnimation.fromValue = 1.0
        alphaAnimation.toValue = 0.0
        alphaAnimation.duration = 0.1
        alphaAnimation.beginTime = dropDuration - 1 + 0.9
        alphaAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
        alphaAnimation.isRemovedOnCompletion = false
        alphaAnimation.fillMode = .forwards
        
        let animations: [CABasicAnimation] = [scaleAnimation, transformAnimation, dropdownAnimation, alphaAnimation]
        let animationGroup = CAAnimationGroup()
        animationGroup.delegate = self
        animationGroup.beginTime = CACurrentMediaTime()
        animationGroup.duration = dropDuration
        animationGroup.animations = animations
        animationGroup.isRemovedOnCompletion = false
        animationGroup.fillMode = .forwards
        waterDropLayer.add(animationGroup, forKey: "animationGroup")
    }
}
extension HUMWaterView: CAAnimationDelegate {
    func animationDidStart(_ anim: CAAnimation) {
    }
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        if waterDropArray.count > 0 {
            if waterDropSoundOn {
                playWaterDropSound()
            }
            startWaterWaveAnimation()
            let waterDropLayer = waterDropArray.removeFirst()
            waterDropLayer.removeFromSuperlayer()
        }
    }
}
extension HUMWaterView: AVAudioPlayerDelegate {
    func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
    }
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
    }
}

